//array, like std:vector but allocated once, and w/o run-time checking
//$Id: matrix2d.h 646 2011-02-04 17:04:10Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/reflection.h"

template<class T>
class Matrix2D
{
protected:
    T*      pp_;
    size_t  rows_;
    size_t  columns_;
public:
    typedef T value_type;
public:
    Matrix2D(): pp_(NULL), rows_(0), columns_(0)
    {}

    explicit Matrix2D(size_t columns, size_t rows):
        pp_(rows * columns ? new T[rows * columns] : NULL),
        rows_(rows),
        columns_(columns)
    {
        COMPILETIME_CHECK(!meta::is_class<T>::result, T_can_not_be_a_class);
    }

    Matrix2D(Matrix2D const& right): pp_(NULL)
    {
        rows_    = right.rows_;
        columns_ = right.columns_;
        size_t dim = rows_ * columns_;
        if (dim > 0)
        {
            pp_ = new T[dim];
            memcpy(pp_, right.pp_, dim * sizeof(T));
        }
    }

    ~Matrix2D()
    {
        delete[] pp_;
    }

    Matrix2D& operator = (Matrix2D const& right)
    {
        if (this != &right)
        {
            Matrix2D tmp(right);
            tmp.swap(*this);
        }
        return *this;
    }

    void swap(Matrix2D& right)
    {
        std::swap(pp_,      right.pp_);
        std::swap(rows_,    right.rows_);
        std::swap(columns_, right.columns_);
    }

//.............................................................................
#if defined(DEBUG) || defined(_DEBUG)
    template<class X>
    class TRow
    {
        X* row_;
        size_t columns_;
        friend class Matrix2D<T>;
//.............................................................................
        TRow(X* row, size_t columns): row_(row), columns_(columns)
        {}
    public:
//.............................................................................
        X& operator[](size_t j)
        {
            ASSERT_(j < columns_);
            return row_[j];
        }
    };
//.............................................................................
    TRow<T> operator[](size_t i)
    {
        return TRow<T>(get_Row(i), columns_);
    }
//.............................................................................
    TRow<T const> operator[](size_t i) const
    {
        return TRow<T const>(get_Row(i), columns_);
    }
#else
//.............................................................................
    T* operator[](size_t i)
    {
        return get_Row(i);
    }
//.............................................................................
    T const* operator[](size_t i) const
    {
        return get_Row(i);
    }
#endif
//.............................................................................
    T const* get_Row(size_t i) const
    {
        ASSERT_(i < rows_);
        return &pp_[i * columns_];
    }
//.............................................................................
    T* get_Row(size_t i)
    {
        ASSERT_(i < rows_);
        return &pp_[i * columns_];
    }
//.............................................................................
    size_t get_Rows() const
    {
        return rows_;
    }
//.............................................................................
    size_t get_Columns() const
    {
        return columns_;
    }
//.............................................................................
    void set_Rows(size_t rows)
    {
        resize_(rows, columns_);
    }
//.............................................................................
    void set_Columns(size_t columns)
    {
        resize_(rows_, columns);
    }
//.............................................................................
    void set_Resolution(size_t rows, size_t columns)
    {
        resize_(rows, columns);
    }
//.............................................................................
    void fill(T const& t)
    {
        size_t dim = rows_ * columns_;
        std::fill(pp_, pp_ + dim, t);
    }
//.............................................................................
    void print(std::ostream& os) const
    {
        using namespace std;
        os << "M x N = " << columns_ << " x " << rows_ << lf;
        for (size_t i = 0; i < rows_; ++i) 
        {
            T const* r = get_Row(i);
            for(size_t j = 0; j < columns_; ++j)
            {
                if (Is_Infty(r[j]))
                    os << setw(8) << "@@";
                else
                    os << setw(8) << fixed << setprecision(2) << r[j];
            }
            os << lf;
        }
    }
//.............................................................................
//.............................................................................
protected:
    void resize_(size_t rows, size_t columns)
    {
        if (columns != columns_ || rows != rows_)
        {
            Matrix2D<T> t(columns, rows);
            for (size_t i = 0; i < std::min(rows, rows_); ++i) 
            {
                T* tr = t.get_Row(i);
                T* r = get_Row(i);
                for(size_t j = 0; j < std::min(columns, columns_); ++j)
                {
                    tr[j] = r[j];
                }
            }
            t.swap(*this);
        }
    }
//.............................................................................
};

namespace meta
{

template<class X>
struct is_visitable< Matrix2D<X> >
{
    enum {result = 0};
};

}
//EOF!
